package com.ljc.listener;


import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.google.gson.Gson;
import com.ljc.common.ErrorCode;
import com.ljc.exception.BusinessException;
import com.ljc.model.dto.UnLockAvailablePiecesDTO;
import com.ljc.model.entity.Order;
import com.ljc.model.enums.OrderStatusEnum;
import com.ljc.service.InnerInterfaceChargingService;
import com.ljc.service.OrderService;
import com.rabbitmq.client.Channel;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;

import static com.ljc.constant.RabbitMqConstant.ORDER_TIMEOUT_QUEUE_NAME;
import static com.ljc.constant.RedisConstant.*;


/**
 * 订单超时监听
 */
@Component
@Slf4j
public class OrderTimeOutListener {

    @Resource
    private OrderService orderService;
    @DubboReference
    private InnerInterfaceChargingService interfaceChargingService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private RedissonClient redissonClient;

    /**
     * 监听死信队列
     */
    @GlobalTransactional(rollbackFor = Exception.class)
    @RabbitListener(queues = ORDER_TIMEOUT_QUEUE_NAME)
    public void delayListener(String orderJson, Message message, Channel channel) throws IOException {
        try {
            Order order = new Gson().fromJson(orderJson, Order.class);
            log.error("监听到订单超时死信队列消息==>{}", order);
            Long orderId = order.getId();
            Order dbOrder = orderService.getById(orderId);


            // 更新接口库存时，锁定接口，防止并发更新数据库出现问题
            // 1、创建锁对象
            Long interfaceId = order.getInterfaceId();
            RLock redisLock = redissonClient.getLock(LOCK_INTERFACE_STOCK + interfaceId);
            // 2、尝试获取锁
            try {
                redisLock.lock();
                if (!redisLock.isLocked()) {
                    // 获取锁失败，直接返回失败或者重试
                    log.error("并发获取更新接口库存的锁失败！");
                    throw new BusinessException(ErrorCode.OPERATION_ERROR, "操作频繁！");
                }
                // 没有生成订单，但是库存已经扣了
                if (dbOrder == null) {
                    // 释放库存
                    unLockAvailablePieces(order);
                    stringRedisTemplate.opsForValue().increment(INTERFACE_STOCK + interfaceId, order.getTotal());
                    stringRedisTemplate.delete(USER_PURCHASE + order.getOrderNumber());
                    channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                    return;
                }
                // 订单成功生成，但是超时未支付
                Integer status = dbOrder.getStatus();
                if (OrderStatusEnum.TOBEPAID.getValue() == status) {
                    //超时未支付,更新订单表
                    orderService.update(new UpdateWrapper<Order>().eq("id", orderId).set("status", OrderStatusEnum.FAILURE.getValue()));
                    //解锁库存
                    unLockAvailablePieces(order);
                    stringRedisTemplate.opsForValue().increment(INTERFACE_STOCK + interfaceId, order.getTotal());
                    stringRedisTemplate.delete(USER_PURCHASE + order.getOrderNumber());
                }
                // 剩下一种情况，订单已经被支付，不用处理
            } finally {
                redisLock.unlock();
            }
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            log.error("订单超时死信队列报错：{}", e.getMessage());
            e.printStackTrace();
            channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
        }
    }

    @GlobalTransactional(rollbackFor = Exception.class)
    private void unLockAvailablePieces(Order order) {
        Long count = order.getTotal();
        UnLockAvailablePiecesDTO unLockAvailablePiecesDTO = new UnLockAvailablePiecesDTO();
        unLockAvailablePiecesDTO.setCount(count);
        unLockAvailablePiecesDTO.setInterfaceId(order.getInterfaceId());
        boolean unLockAvailablePieces = interfaceChargingService.unLockAvailablePieces(unLockAvailablePiecesDTO);
        if (!unLockAvailablePieces) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "订单超时解锁库存失败");
        }
    }
}
